package cn.virens.common.easyexcel;

import cn.hutool.core.thread.ThreadUtil;
import cn.idev.excel.context.AnalysisContext;
import cn.idev.excel.read.listener.ReadListener;
import cn.virens.common.easyexcel.dict.ExcelDict;
import cn.virens.common.easyexcel.dict.ExcelDictStack;
import cn.virens.common.mybatis.entity.BaseModel;
import cn.virens.common.mybatis.service.dubbo.BaseWriteProvider;
import cn.virens.common.util.exception.APIException;
import org.dromara.hutool.core.data.id.IdUtil;
import org.dromara.hutool.core.io.IoUtil;
import org.dromara.hutool.core.io.file.FileUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Consumer;

public class ExcelImportExecutor<T extends BaseModel> implements ReadListener<T> {
    protected final Logger logger = LoggerFactory.getLogger(getClass());

    protected final AtomicBoolean state = new AtomicBoolean(false);
    protected final List<Integer> error = new ArrayList<>();
    protected final List<T> answer = new ArrayList<>();

    protected final BaseWriteProvider<T> mBaseService;
    protected final ExcelDictStack dictStack;

    protected final String taskUuid;
    protected final Long taskTime;
    protected final File temp;

    // 完成回调监听
    protected Consumer<List<T>> complete;

    public ExcelImportExecutor(BaseWriteProvider<T> mBaseService) {
        this(mBaseService, IdUtil.fastSimpleUUID());
    }

    public ExcelImportExecutor(BaseWriteProvider<T> mBaseService, String taskUuid) {
        this.dictStack = ExcelDictStack.registerDictStack();
        this.taskTime = System.currentTimeMillis();
        this.temp = FileUtil.createTempFile();
        this.mBaseService = mBaseService;
        this.taskUuid = taskUuid;
    }

    /**
     * 开始对上传内容进行解析
     *
     * @param file  文件
     * @param clazz 实体类型
     */
    public void doRead(MultipartFile file, Class<T> clazz) throws Exception {
        // 创建临时文件，并将上传内容存入文件中----------------------------------
        try (OutputStream outputStream = FileUtil.getOutputStream(temp)) {
            try (InputStream inputStream = file.getInputStream()) {
                IoUtil.copy(inputStream, outputStream);
            }
        }

        // 开启异步线程，执行数据读取操作，读取完成后需要删除临时文件--------------
        ThreadUtil.execute(() -> this.doReadSheet(clazz));
    }

    public void doReadSheet(Class<T> clazz) throws APIException {
        try (var work = new ExcelImportWork(this, clazz)) {
            work.builder().useScientificFormat(false);
            work.builder().ignoreEmptyRow(true);
            work.builder().autoTrim(true);

            work.builder().sheet().doRead();
        }
    }

    @Override
    public void onException(Exception exception, AnalysisContext context) {
        this.logger.error("读取出错：{}", exception.getMessage());

        this.error.add(context.readRowHolder().getRowIndex());
    }

    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
        if (complete != null && !state.get()) {
            this.complete.accept(answer);
            this.state.set(true);
        }
    }

    /**
     * 添加转换字典
     */
    public void putDict(ExcelDict dict) throws APIException {
        this.dictStack.putDict(dict);
    }

    @Override
    public void invoke(T data, AnalysisContext context) {
        this.answer.add(mBaseService.insert(data));
    }

    /**
     * 设置任务完成回调监听
     */
    public void setComplete(Consumer<List<T>> complete) throws APIException {
        this.complete = complete;
    }

    /**
     * 错误行列表
     */
    public List<Integer> getError() throws APIException {
        return error;
    }

    /**
     * 删除临时文件
     */
    public void cleanTempFile() throws APIException {
        FileUtil.del(temp);
    }

    /**
     * 判断是否导入完成
     */
    public boolean isComplete() throws APIException {
        return state.get();
    }

    /**
     * 获取任务编号
     */
    public String getTaskUuid() throws APIException {
        return taskUuid;
    }

    /**
     * 获取开始时间
     */
    public Long getTaskTime() throws APIException {
        return taskTime;
    }


    /**
     * 获取已导入数量
     */
    public int getSize() throws APIException {
        return answer.size();
    }
}
